iT邦幫忙

2022 iThome 鐵人賽

DAY 12
0
Software Development

《今天也走在開發遊戲引擎的路上》系列 第 12

「引擎實作」—— 日誌系統 Logging system

  • 分享至 

  • xImage
  •  

Logging

在實作中,當我們在debug時,筆者相信有許多人都會使用「printf大法」來追蹤或顯示程式內部運行的狀況吧,而相信大家接觸許多軟體也有看過日誌系統,用於調試、紀錄程式發生的事件,今天我們就來實現這個Logging system。

實作

那麼我們要來使用spdlog來實現我們的Logging系統,他是一個快速且輕量的開元庫,而且功能豐富,還能使用fmt來實現自訂格式。

安裝

我們開啟終端機使用vcpkg尋找spdlog。

接著我們用vcpkg install spdlog。

vcpkg install spdlog:x64-windows-static

結束後會看見這個畫面,因為待會要將其加入CMake當中,所以我們可以先放著別關掉。

將engine.cmake做更改,加入這兩行

find_package(spdlog REQUIRED)

target_link_libraries(engine
    spdlog::spdlog
    spdlog::spdlog_header_only
)

這裡提供另一種做法,由於spdlog是header_only的函式庫,我們也可以簡單地將其作為submodule加進來專案中。

若是還沒初始化需要先init。

git init

接著將spdlog以submodule加進專案裡。

git submodule add https://github.com/gabime/spdlog engine/vendor/spdlog

如果使用這個做法的話就將engine.cmake的include資料夾加入spdlog的include資料夾。

target_include_directories(engine PRIVATE
    ${CMAKE_SOURCE_DIR}/engine
    ${CMAKE_SOURCE_DIR}/engine/src
    ${CMAKE_SOURCE_DIR}/engine/vendor/spdlog/include
)

RuntimeModule Class

我們在Core資料夾裡新增一個Core.h,裡面新增一個RuntimeModule class,這是我們將來要新增組件管理器的時候需要使用的class,他具備幾個虛擬函式。

  • startUp
    用於啟動這個子系統。

  • shutDown
    用於關閉這個子系統。

  • Tick
    用於每幀更新。

這是Core.h的具體內容。

  • Core.h
#pragma once

namespace Engine {

    class RuntimeModule
    {
    public:
        virtual ~RuntimeModule() {};

        virtual void startUp() = 0;
        virtual void shutDown() = 0;

        virtual void Tick() = 0;
    };

}

Log

接著我們在engine的src資料夾裡新增一個Log的資料夾,並建立Log.h與Log.cpp。

定義LogManager並且繼承自Core的運行時組件。我們也能使用macro來簡化Log的使用。

  • Log.h
#include "Core/Core.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/fmt/ostr.h"

namespace Engine {

    class __declspec(dllexport) LogManager: public Engine::RuntimeModule
    {
    public:
        ~LogManager() {};

        static void startUp() ;
        static void shutDown() ;
        void Tick();
        
        inline static std::shared_ptr<spdlog::logger>& GetCoreLogger() { return s_CoreLogger; }
    private:
        static std::shared_ptr<spdlog::logger> s_CoreLogger;
    };

// Core Log macro
#define ENGINE_CORE_TRACE(...) ::Engine::LogManager::GetCoreLogger()->trace(__VA_ARGS__);
#define ENGINE_CORE_INFO(...) ::Engine::LogManager::GetCoreLogger()->info(__VA_ARGS__);
#define ENGINE_CORE_WARN(...) ::Engine::LogManager::GetCoreLogger()->warn(__VA_ARGS__);
#define ENGINE_CORE_ERROR(...) ::Engine::LogManager::GetCoreLogger()->error(__VA_ARGS__);
#define ENGINE_CORE_FATAL(...) ::Engine::LogManager::GetCoreLogger()->fatal(__VA_ARGS__);
}

使用set_pattern來設定自定義的格式,具體文檔在這裡

這裡稍微整理一些常用的。

flag meaning example
%v The actual text to log "log text"
%T or %X ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S "23:55:59"
%n Logger's name "logger name"
  • Log.cpp
#include "Log.h"
#include "spdlog/sinks/stdout_color_sinks.h"

namespace Engine {
    std::shared_ptr<spdlog::logger> LogManager::s_CoreLogger;

    void LogManager::startUp()
    {
        // 自定義格式: [時間] Logger名稱:Log訊息
        spdlog::set_pattern("%^[%T] %n: %v%$");
        
        // Logger名稱
        s_CoreLogger = spdlog::stdout_color_mt("ENGINE");
        
        // 低於設定的Level級別的Log將不會被顯示
        s_CoreLogger->set_level(spdlog::level::trace);
    }

    void LogManager::shutDown()
    {
        s_CoreLogger->flush();
        spdlog::drop_all();
    }

    void LogManager::Tick()
    {

    }
}

使用

我們可以在引擎啟動時使用Log來輸出訊息。就可以完成了!

  • Application.cpp
#include "Application.h"
#include "Log/Log.h"

namespace Engine {
    Application::Application() {}
    Application::~Application() {}

    void Application::Run()
    {
        ENGINE_CORE_TRACE("{0}","Engine Ver 1.0.0 !");
        while (true)
        {
            /* code */
        }
    };
}

後記

/images/emoticon/emoticon04.gif


上一篇
「引擎實作」—— 入口點 Entry point
系列文
《今天也走在開發遊戲引擎的路上》12
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言